
import tkinter as tk
import os
from tkinter import filedialog, messagebox, Menu, Label, END
from pygments.lexers import PythonLexer  # 导入Python lexer
from pygments.styles import get_style_by_name
# 导入pyjd库功能
try:
    from pyjd import *
except ImportError:
    # 如果未安装，尝试从当前目录导入
    import pyjd
from cx_Freeze import setup, Executable
import threading
import subprocess  # 确保subprocess模块在文件顶部导入
import sys  # 确保sys模块已导入

class ArtCodeEditor:
    def __init__(self, root):
        self.root = root
        self.root.title("Art Code for Python - pyjd编辑器")
        
        # 设置窗口大小并更新任务
        self.root.geometry("800x600")
        self.root.update_idletasks()  # 确保窗口尺寸更新

        # 创建文本编辑区域
        self.text_area = tk.Text(self.root, undo=True)
        self.code_text = self.text_area  # 添加code_text引用以便定位错误行
        self.text_area.pack(fill=tk.BOTH, expand=1)
        
        # 初始化错误行样式
        self.text_area.tag_config("error", background="red", foreground="white")

        # 初始化语法高亮样式
        self.lexer = PythonLexer()
        self.style = get_style_by_name("default")
        self._setup_syntax_highlighting()

        # 创建菜单栏
        self.menu_bar = tk.Menu(self.root)
        self.root.config(menu=self.menu_bar)

        # 文件菜单
        file_menu = tk.Menu(self.menu_bar, tearoff=0)
        file_menu.add_command(label="新建", command=self.new_file)
        file_menu.add_command(label="打开", command=self.open_file)
        file_menu.add_command(label="保存", command=self.save_file)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit)
        self.menu_bar.add_cascade(label="文件", menu=file_menu)

        # 编辑菜单
        edit_menu = tk.Menu(self.menu_bar, tearoff=0)
        edit_menu.add_command(label="撤销", command=self.text_area.edit_undo)
        edit_menu.add_command(label="重做", command=self.text_area.edit_redo)
        edit_menu.add_separator()
        edit_menu.add_command(label="剪切", command=lambda: self.text_area.event_generate("<<Cut>>"))
        edit_menu.add_command(label="复制", command=lambda: self.text_area.event_generate("<<Copy>>"))
        edit_menu.add_command(label="粘贴", command=lambda: self.text_area.event_generate("<<Paste>>"))
        edit_menu.add_command(label="删除", command=lambda: self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST) if self.text_area.tag_ranges(tk.SEL) else None)
        edit_menu.add_command(label="全选", command=lambda: self.text_area.tag_add(tk.SEL, "1.0", tk.END))
        self.menu_bar.add_cascade(label="编辑", menu=edit_menu)

        # 关于菜单
        about_menu = tk.Menu(self.menu_bar, tearoff=0)
        about_menu.add_command(label="关于", command=self.show_about)
        self.menu_bar.add_cascade(label="关于", menu=about_menu)

        # 绑定文件操作事件
        self.text_area.bind("<<Modified>>", lambda e: self.update_status_bar())
        self.root.bind("<Configure>", lambda e: self.update_status_bar())

        # 初始化文件路径
        self.file_path = None
        
        # 创建状态栏
        self.create_status_bar()
        
        # 添加构建功能
        self.add_build_functionality()
        
        # 添加运行功能
        self.add_run_functionality()

    def _setup_syntax_highlighting(self):
        """配置语法高亮样式"""
        styles = self.style.styles
        for token, color in styles.items():
            # 将 token 转换为字符串并严格检查是否包含特殊格式
            token_str = str(token)
            if any(fmt in token_str for fmt in ['italic', 'bold', 'underline']):
                continue
            
            # 提取前景色，处理可能的背景色部分
            fg_color = color.split(' ')[0] if ' ' in color else color
            
            # 确保颜色值是有效的（以 # 开头的RGB值或命名颜色）
            if fg_color.startswith('bg:') or fg_color == '' or not fg_color.startswith('#') and not fg_color in ['red', 'green', 'blue', 'orange', 'purple', 'black', 'white']:
                continue
                
            self.text_area.tag_configure(token_str, foreground=fg_color)

        self.text_area.bind("<KeyRelease>", self._highlight_code)

    def _highlight_code(self, event=None):
        """高亮显示代码"""
        code = self.text_area.get("1.0", tk.END)
        tokens = self.lexer.get_tokens(code)

        self.text_area.mark_set("matchStart", "1.0")
        self.text_area.mark_set("matchEnd", "1.0")
        self.text_area.mark_set("searchLimit", tk.END)

        for token, value in tokens:
            index = self.text_area.index("matchEnd")
            pos = self.text_area.search(value, index, stopindex=tk.END)
            if pos:
                self.text_area.mark_set("matchStart", pos)
                self.text_area.mark_set("matchEnd", f"{pos}+{len(value)}c")
                self.text_area.tag_add(str(token), "matchStart", "matchEnd")

    def create_menu(self):
        """创建菜单栏"""
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="新建", command=self.new_file)
        file_menu.add_command(label="打开", command=self.open_file)
        file_menu.add_command(label="保存", command=self.save_file)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit)
        
        # 添加pyjd专用菜单
        pyjd_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="pyjd功能", menu=pyjd_menu)
        pyjd_menu.add_command(label="运行pyjd程序", command=self.run_pyjd_program)
        pyjd_menu.add_command(label="显示欢迎动画", command=self.show_welcome_animation)
        pyjd_menu.add_separator()
        pyjd_menu.add_command(label="立即关机", command=lambda: 立即关机())
        pyjd_menu.add_command(label="立即重启", command=lambda: 立即重启())
        pyjd_menu.add_command(label="系统命令测试", command=self.test_system_commands)

    def create_text_area(self):
        """创建文本编辑区域"""
        self.text_area = tk.Text(self.root, wrap="word", undo=True)
        self.text_area.pack(fill="both", expand=True)

    def create_status_bar(self):
        """创建状态栏"""
        self.status_bar = tk.Label(self.root, text="准备就绪", bd=1, relief=tk.SUNKEN)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 添加用于显示运行状态的标签
        self.run_status = tk.Label(self.root, text="", bd=1, relief=tk.SUNKEN)
        self.run_status.pack(side=tk.BOTTOM, fill=tk.X)

    def new_file(self):
        """创建新文件"""
        if self.text_area.edit_modified():
            if messagebox.askyesno("新建文件", "当前内容未保存，是否继续？"):
                self.text_area.delete(1.0, tk.END)
                self.file_path = None
                self.update_status_bar()
                self.text_area.edit_modified(False)

    def open_file(self):
        """打开文件"""
        file_path = filedialog.askopenfilename(filetypes=[
            ("Art Code 文件", "*.ar"),
            ("Python files", "*.py"),
            ("Text files", "*.txt"),
            ("All files", "*.*")
        ])
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as file:
                    self.text_area.delete(1.0, tk.END)
                    self.text_area.insert(tk.END, file.read())
                self.file_path = file_path
                self.update_status_bar()
                self._highlight_code()
            except Exception as e:
                messagebox.showerror("错误", f"无法打开文件: {str(e)}")

    def save_file(self):
        """保存文件"""
        if self.file_path:
            try:
                with open(self.file_path, 'w', encoding='utf-8') as file:
                    file.write(self.text_area.get(1.0, tk.END))
                self.text_area.edit_modified(False)  # 重置修改标志
                messagebox.showinfo("保存", "文件保存成功")
            except Exception as e:
                messagebox.showerror("错误", f"无法保存文件: {str(e)}")
        else:
            self.save_file_as()

    def save_file_as(self):
        """另存为文件"""
        file_path = filedialog.asksaveasfilename(defaultextension=".py",
                                                  filetypes=[
                                                      ("Art Code 文件", "*.ar"),
                                                      ("Python files", "*.py"),
                                                      ("Text files", "*.txt"),
                                                      ("All files", "*.*")
                                                  ])
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as file:
                    file.write(self.text_area.get(1.0, tk.END))
                self.file_path = file_path
                self.update_status_bar()
                messagebox.showinfo("保存", "文件保存成功")
            except Exception as e:
                messagebox.showerror("错误", f"无法保存文件: {str(e)}")

    def update_status_bar(self):
        """更新状态栏信息"""
        if self.file_path:
            file_name = os.path.basename(self.file_path)
            file_size = os.path.getsize(self.file_path)
            self.status_bar.config(text=f"当前文件: {file_name}  大小: {file_size} 字节")
        else:
            self.status_bar.config(text="新建文件")

    def show_about(self):
        """显示关于信息"""
        about_text = "Art Code for Python - 版本 1.0\n\n一个的Python代码编辑器\n\n使用Tkinter和Pygments构建\n\n主要功能：\n- 新建、打开、保存文件\n- 文本输入与编辑\n- 多语言语法高亮\n- 状态栏显示路径或相关信息"
        messagebox.showinfo("关于 Art Code for Python", about_text)
        
    def run_pyjd_program(self):
        """运行当前编辑的pyjd程序"""
        if not self.file_path:
            messagebox.showwarning("警告", "请先保存文件")
            return
            
        try:
            # 尝试执行当前文件
            import subprocess
            result = subprocess.run([sys.executable, self.file_path], 
                                  capture_output=True, 
                                  text=True,
                                  creationflags=subprocess.CREATE_NEW_CONSOLE)
            
            # 显示输出结果
            output_window = tk.Toplevel(self.root)
            output_window.title("pyjd程序输出")
            output_text = tk.Text(output_window)
            output_text.pack(fill=tk.BOTH, expand=1)
            output_text.insert(tk.END, result.stdout)
            output_text.insert(tk.END, result.stderr)
            
        except Exception as e:
            messagebox.showerror("错误", f"无法运行程序: {str(e)}")

    def show_welcome_animation(self):
        """显示pyjd欢迎动画"""
        animation_window = tk.Toplevel(self.root)
        animation_window.title("pyjd欢迎动画")
        animation_window.geometry("600x400")
        
        # 在单独的线程中运行动画，避免阻塞UI
        import threading
        def run_animation():
            try:
                # 重定向输出到文本控件
                import sys
                from io import StringIO
                
                output = StringIO()
                sys.stdout = output
                
                # 运行走马灯动画
                走马灯()
                
                # 恢复标准输出
                sys.stdout = sys.__stdout__
                
                # 在UI线程更新界面
                animation_window.after(0, lambda: [
                    animation_text.insert(tk.END, output.getvalue()),
                    animation_text.config(state=tk.DISABLED)
                ])
                
            except Exception as e:
                animation_window.after(0, lambda: messagebox.showerror("错误", f"动画运行失败: {str(e)}"))

        animation_text = tk.Text(animation_window)
        animation_text.pack(fill=tk.BOTH, expand=1)
        
        thread = threading.Thread(target=run_animation)
        thread.start()

    def test_system_commands(self):
        """测试pyjd系统命令功能"""
        test_window = tk.Toplevel(self.root)
        test_window.title("pyjd系统命令测试")
        test_window.geometry("400x300")
        
        tk.Label(test_window, text="选择要测试的功能：").pack(pady=10)
        
        btn_frame = tk.Frame(test_window)
        btn_frame.pack(pady=10)
        
        tk.Button(btn_frame, text="立即关机", command=lambda: 立即关机()).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="立即重启", command=lambda: 立即重启()).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="立即注销", command=lambda: 立即注销()).pack(side=tk.LEFT, padx=5)
        
        tk.Label(test_window, text="延时操作（秒）：").pack(pady=10)
        
        delay_frame = tk.Frame(test_window)
        delay_frame.pack(pady=10)
        
        self.delay_entry = tk.Entry(delay_frame, width=10)
        self.delay_entry.pack(side=tk.LEFT, padx=5)
        
        tk.Button(delay_frame, text="延时关机", command=self.delay_shutdown).pack(side=tk.LEFT, padx=5)
        tk.Button(delay_frame, text="延时重启", command=self.delay_restart).pack(side=tk.LEFT, padx=5)
        tk.Button(delay_frame, text="取消操作", command=lambda: 取消关机()).pack(side=tk.LEFT, padx=5)
        
    def delay_shutdown(self):
        try:
            seconds = int(self.delay_entry.get())
            延时关机(seconds)
        except ValueError:
            messagebox.showerror("错误", "请输入有效的数字")
            
    def delay_restart(self):
        try:
            seconds = int(self.delay_entry.get())
            延时重启(seconds)
        except ValueError:
            messagebox.showerror("错误", "请输入有效的数字")

    def add_build_functionality(self):
        """添加构建功能"""
        build_menu = tk.Menu(self.menu_bar, tearoff=0)
        build_menu.add_command(label="构建可执行文件", command=self.build_executable)
        self.menu_bar.add_cascade(label="构建", menu=build_menu)

    def build_executable(self):
        """构建可执行文件"""
        if not self.file_path:
            messagebox.showwarning("警告", "请先保存文件")
            return

        try:
            # 构建可执行文件
            base = None
            if sys.platform == "win32":
                base = "Win32GUI"

            executables = [Executable(self.file_path, base=base)]
            setup(
                name=os.path.splitext(os.path.basename(self.file_path))[0],
                version="1.0",
                description="Art Code for Python - pyjd编辑器",
                executables=executables,
                author="Art Code Team",
                options={
                    "build_exe": {
                        "packages": ["tkinter", "pygments"],
                        "include_files": []
                    }
                }
            )

            messagebox.showinfo("构建成功", "可执行文件已构建完成")
        except Exception as e:
            messagebox.showerror("错误", f"构建失败: {str(e)}")

    def add_run_functionality(self):
        """添加运行功能，创建运行菜单项"""
        run_menu = tk.Menu(self.menu_bar, tearoff=0)
        run_menu.add_command(label="运行代码", command=self.run_code)
        self.menu_bar.add_cascade(label="运行", menu=run_menu)

    def run_code(self):
        """执行当前编辑的文件"""
        if not self.file_path:
            messagebox.showwarning("警告", "请先保存文件")
            return

        # 检查文件扩展名是否为.py或.ar
        if not (self.file_path.endswith(".py") or self.file_path.endswith(".ar")):
            messagebox.showerror("错误", "只能运行Python脚本文件（.py）或Art Code文件（.ar）")
            return

        try:
            # 创建输出窗口
            output_window = tk.Toplevel(self.root)
            output_window.title("代码输出 - " + os.path.basename(self.file_path))
            output_window.geometry("600x400")  # 设置合理的窗口大小
            
            # 创建文本框和滚动条
            output_text = tk.Text(output_window, wrap=tk.NONE)
            scrollbar = tk.Scrollbar(output_window, orient="vertical", command=output_text.yview)
            output_text.configure(yscrollcommand=scrollbar.set)
            
            # 布局组件
            output_text.pack(side="left", fill="both", expand=True)
            scrollbar.pack(side="right", fill="y")
            
            # 添加状态标签
            status_label = tk.Label(output_window, text="输出完成", bd=1, relief=tk.SUNKEN)
            status_label.pack(side="bottom", fill="x")
            
            # 在独立线程中运行代码
            import threading
            thread = threading.Thread(target=self._execute_code, args=(output_text, output_window))
            thread.start()
        except Exception as e:
            messagebox.showerror("错误", f"无法启动代码运行: {str(e)}")

    def _execute_code(self, output_widget, window):
        """实际执行代码的方法，在独立线程中调用"""
        try:
            # 执行文件并捕获输出
            result = subprocess.run(
                [sys.executable, self.file_path],
                capture_output=True,
                text=True,
                check=True  # 如果子进程返回非零退出码将抛出异常
            )
            
            # 在UI线程更新输出
            output_widget.insert(tk.END, result.stdout)
            output_widget.insert(tk.END, result.stderr)
            
            # 更新状态栏
            self.run_status.config(text=f"最近运行: {os.path.basename(self.file_path)} 退出码: {result.returncode}")
            
        except subprocess.CalledProcessError as e:
            # 子进程返回非零退出码
            error_message = f"程序异常退出，错误码: {e.returncode}\n"
            error_message += e.stdout if e.stdout else e.stderr if e.stderr else "未知错误"
            
            # 尝试解析错误信息中的行号
            line_number = self._parse_error_line(e.stderr or e.stdout)
            if line_number:
                # 高亮显示错误行
                self.code_text.tag_add("error", f"{line_number}.0", f"{line_number}.end+1c")
                self.code_text.see(f"{line_number}.0")
            
            # 将错误信息发布到状态栏而不是弹出对话框
            self.run_status.config(text=f"错误: {error_message}")
            
            # 插入错误信息到输出窗口
            output_widget.insert(tk.END, error_message)
            
        except Exception as e:
            # 其他类型的异常
            error_message = f"运行时错误: {str(e)}\n请检查Python环境是否正常安装"
            # 将错误信息发布到状态栏而不是弹出对话框
            self.run_status.config(text=f"错误: {error_message}")
            
            # 插入错误信息到输出窗口
            output_widget.insert(tk.END, error_message)
            
        finally:
            # 禁用输出文本框的编辑
            output_widget.config(state=tk.DISABLED)

    def _parse_error_line(self, error_output):
        """解析错误输出，提取错误发生的行号"""
        if not error_output:
            return None
            
        # 查找文件行号信息，典型格式：File "filename", line X, in <module>
        import re
        match = re.search(r"line (\d+),", error_output)
        if match:
            return int(match.group(1))
        return None

if __name__ == "__main__":
    root = tk.Tk()
    editor = ArtCodeEditor(root)
    root.mainloop()
